/* ToDo's
   ------
   1) Write clear comments - Rami please verify with complex main
   2) Change all 'using namespace std' to using <identifier>
      in order to avoid polluting scope.
   3) Polynom.h and Monom.h - Avoid copying large data structures
      check if it's possible to return references/pointers instead of objects
   4) Monom.h - is Destructor & Copy Constructor needed ? 
				the default ones provided by C++ are enough
   5) Monom.h - do we need to overload operator <double> + Monom ?
   6) Monom.h - ostream& operator<<
	  Optional - change variables 'printInt' and 'printC' from int to bool

 */


/* hw1 - main cpp file
   -------------------
   main program file

 */
 
#include <iostream>
#include <string>       // used for 4 digits after precision point
#include "Monom.h"
#include "Polynom.h"

/* DEBUGMODE == 0 - this will call normal main
   DEBUGMODE == 1 - this will call complex main
 */
#define DEBUGMODE 1

using namespace std;

void stam();
void doTest();
void doTest2();

int main()
{
    if (DEBUGMODE == 0) {
        // Main from HW
        Monom a, b(4.4), c(3.5, 5), d=0, e(5.9, 5), f(-1.0, 1);
        if (b != c || c==d)
        {
            cout << b << endl;  // prints 4.4
            cout << c << endl;  // prints 3.5x^5
            cout << f << endl;  // prints -x
        }
        e = e / b;
        e = e * b;
        d = b * c / 2.0;
        d *= (c + e) * 3.6;
        d = c ^ 7;
        d = 1.0;
        d+= (e - c).getExponent();  // power x^3 --> 3
        cout << d << endl;  // print 6
        cout << c(3.1) << endl; // this will print 1002.0203
        Monom m = c.derive();
        cout << c << endl; // print 17.5x^4

	    Polynom p, q(c);
	    Polynom r(p);
	    p = f + q;
	    cout << p << endl;	//prints 17.5x^4 -x
	    p = p - q;
	    cout << p << endl;	//prints -x
	    cout << p.getOrder() + 1 << endl; // prints 2
    } else {
//		stam();
		/*
        Monom c(-2.5,1), d(1,3), e(-5,0), f(-2.5,7), g(4,2), h(123,6);
        Polynom p(c);
        p = p + d + e + f;
        cout << p << endl; // prints -5 -2.5x +x^3 -2.5x^7
        p = p + 5;
        cout << p << endl; // prints -2.5x +x^3 -2.5x^7
        p = p - d;
        cout << p << endl; // prints -2.5x -2.5x^7
        p = p - f;
        cout << p << endl; // prints -2.5x
        Polynom q(c);
        p = q + d + e + f + g + h;
        cout << p << endl; // prints -5 -2.5x +4x^2 +x^3 +123x^6 -2.5x^7
        Monom xc(-121,6);
        p = p + xc;
        cout << p << endl; // prints -5 -2.5x +4x^2 +x^3 +2x^6 -2.5x^7
        Monom mm(1,9);
        p = p - mm;
        cout << p << endl; // prints -5 -2.5x +4x^2 +x^3 +2x^6 -2.5x^7 -x^9
        Monom ml(-1,5);
        p = p + ml;
        cout << p << endl; // prints -5 -2.5x +4x^2 +x^3 -x^5 +2x^6 -2.5x^7 -x^9
        p = p - g;
        cout << p << endl; // prints -5 -2.5x +x^3 -x^5 +2x^6 -2.5x^7 -x^9
        p = p + mm;
        cout << p << endl; // prints -5 -2.5x +x^3 -x^5 +2x^6 -2.5x^7
        p = p + 5 - ml;
        cout << p << endl; // prints -2.5x +x^3 +2x^6 -2.5x^7
        p = p.derive();
        cout << p << endl; // prints -2.5 +3x^2 +12x^5 -17.5x^6
        p = p.derive();
        cout << p << endl; // prints 6x +60x^4 -105x^5
        p = p.derive();
        p = p.derive();
        p = p.derive();
        p = p.derive();
        p = p.derive();
//        p = p.derive();
        cout << p << endl; // prints -702.5
        p = p.derive();
        cout << p << endl; // prints 0
        p = p.derive();
        p = p.derive();
        p = p.derive();
        p = p.derive();
        p = p.derive();
        cout << p << endl; // prints 0

//        p = p + g + h;
//        cout << p << endl; // prints -2.5x +4x^2 +x^3 +123x^6 -2.5x^7
 //       p = p - g;
 //       cout << p << endl; // prints -2.5x +x^3 +123x^6 -2.5x^7
//        p = p - p;
//        cout << p << endl; // prints 0
        /*
        cout << p << endl; // prints -2.5x +x^3 +123x^6 -2.5x^7
        h.setC(-123);
        p = p + h;
        cout << p << endl; // prints -2.5x +x^3 -2.5x^7
        */
//		doTest();		// Amir's and Ofir's test
		doTest2();		// Zero test
    }
    system("pause");
	return 0;
}

void doTest()
{
	// Monom's 0 test
	Monom a, b(0), c(0, 0);
	cout << "a= " << a << " b= " << b << " c= " << c << endl; // prints 0

	a = b = c = b;
	if (a != b || a == c)
	{
		c += 0;
		cout << "c: " << c << endl;		// prints c = 0
		c *= a * b * c;
		cout << "c: " << c << endl;		// prints c = 0
		cout << "C = " << c.getExponent() << endl;	// prints C = 0
		cout << "C = " << c.derive() << endl;		// prints C = 0
		c += 7718;
		cout << "C = " << c << endl;				// prints C = 7718
		cout << "C = " << c.getExponent() << endl;   // prints C = 0
		cout << "C = " << c.derive() << endl;		// prints C = 0
		Monom d(c);
		d = 100;
		cout << d(5) << endl;	// prints 100
		Monom e(400,2);
		d = e;
		cout << d(3) << endl;	// prints 3600
		d = d.derive();
		cout << d.derive() << endl;	// 800x
		e = e ^ 1;
		cout << e << endl; //prints 400x^2
		cout << e.getExponent() - 10 << endl; // prints -8
		e = (e ^ 0) * e;
		cout << e << endl;	//prints 400x^2
		Monom f(4,2), g(6,9), h(1,12), i(5,1);
		f = (f * g) + (h / i);	 // 24x^11 + 1/5x^11
		cout << "f = " << f << endl;	// prints f = 24.2x^11
		cout << f * 4 * 8.5 * 2 << endl;	// prints f = 1645.6x^11
		f = f * 4 * 8.5 * 2;
		Monom j(1645.6,11);
		f = f - j;
		cout << "f = " << f << endl;	// prints f = 0
		f = j;
	}

	// Polynom testing
	
	Monom f(1645.6,11);

	Polynom p1(f);
	cout << p1 << endl;	// prints 1645.6x^11
	p1 = p1 + f + p1;
	cout << p1 << endl;	// prints 4936.8
	p1 = p1 - (f*3);
	cout << p1 << endl;	// prints 0

	Monom m1(3,5), m2(2,1), m3(4,8), m4(-6.3,5), m5(10,92), m6(7,6);
	Polynom p2(m1);
	p2 = p2 + m2 + m3 + m4 + m5 + m6;
	cout << p2 << endl;			// prints 2x -3.3x^5 +7x^6 +4x^8 +10x^92
	p2 = p2 - m6;
	cout << p2 << endl;			// prints 2x -3.3x^5 +4x^8 +10x^92
	p2.derive();
	cout << p2 << endl;			// prints 2 -16.5x^4 +32x^7 +920x^91

	Polynom *p3 = new Polynom(p2);
	p2 = p3->derive();
	cout << p2 << endl;			// prints -66x^3 +224x^6 +83720x^90
	delete p3;
	cout << p2 << endl;			// prints -66x^3 +224x^6 +83720x^90
	p2 = p1 + p2 + 0;
	cout << p2 << endl;			// prints -66x^3 +224x^6 +83720x^90
	p2 = p2 - 0;
	cout << p2 << endl;			// prints -66x^3 +224x^6 +83720x^90
	Monom m7(-224,6);
	p2 = m7 + p2;
	cout << p2 << endl;			// prints -66x^3 +83720x^90
	Monom m8(-100,-350);
	p2 = p2 + m7 + m8;
	cout << p2 << endl;			// prints -100x^-350 -66x^3 -224x^6 +83720x^90
	p2 = 0 - p2;
	cout << p2 << endl;			// prints 100x^-350 +66x^3 +224x^6 -83720x^90
	cout << p2.getOrder() << endl;		// prints 90
	p2 = p2 + m8;
	cout << p2 << endl;			// prints 66x^3 +224x^6 -83720x^90
	Monom m9(-83720,90);
	p2 = p2 - m9;
	cout << p2 << endl;			// prints 66x^3 +224x^6


	p2 = p2.derive();			// 198x^2 +1344x^5
	cout << p2 << endl;			

	p2 = p2.derive();			
	cout << p2 << endl;			// prints 396x + 6720x^4

	p2 = p2.derive();
	cout << p2 << endl;			// prints 396 +26880x^3

	p2 = p2.derive();
	cout << p2 << endl;			// prints 80640x^2

	cout << (Polynom(Monom(2,4)) + Polynom(Monom(-2,4))).getOrder() << endl; // prints 0
	cout << (p2 - p2).getOrder() << endl;	//prints 2 - after fix: 0
	cout << (Monom(4,5) + Monom(-4,5)).getExponent() << endl;	// prints 5, after fix: 0

	p2 = p2.derive();
	cout << p2 << endl;			// prints 161280x
	

	p2 = p2.derive();
	cout << p2 << endl;			// prints 161280


 	p2 = p2.derive();
	cout << p2 << endl;			// 0

	cout << p2.getOrder() << endl;			// prints 0

}

void doTest2()
{
	// In the beginning
	cout << ((Polynom(Monom(4,3)) + Polynom(Monom(-5,0))) + Polynom(Monom(-4,3))).getOrder() << endl; // prints 0

	// In the middle
	Polynom p1 = ((Polynom(Monom(1,2)) + Monom(2,1)) + Monom(6,0)); // 6 + 2x + x^2
	Polynom p2 = (((Polynom(Monom(5,4)) + Monom(1,3)) + Monom(-2,1)) + Monom(10,0)); // 10 -2 +x^3 +5x^4
	cout << ((p1 + p2) - (p1 + p2)).getOrder() << endl;
//	cout << ((Polynom(Monom(1,2)) + Monom(2,1)) + Monom(6,0)) << endl; // prints 6 + 2x + x^2

	// In the end
	cout << ((Polynom(Monom(2,0)) + Polynom(Monom(6,3))) - Polynom(Monom(6,3))).getOrder() << endl; // prints 0

}


void stam()
{

	Monom m1,m2=0,m3(0,0),m4(0,12),m5(7,0),m6(2,2),m7(-2,2),m8(3.5,5),m9(8,0),m10;
	Polynom p1,p2(m5),p3(m7),p4(m8),p5(p1),p6(p3),p7(p5),p8(p6);

	cout << "m1 = " << m1 << endl;	// m1 = 0
	cout << "m1' = " << m1.derive() << endl; //m1 = 0

	cout << "m4 = " << m4 << endl;	// m4 = 0
	cout << "m4' = " << m4.derive() << endl; // m4 = 0

	cout << "m5 = " << m5 << endl;			// m5 = 7
	cout << "m5' = " << m5.derive() << endl; // m5 = 0;

	cout << "m8 = " << m8 << endl;			// m8 = 3.5x^5
	cout << "m8' = " << m8.derive() << endl; // m8' = 17.5x^4

	m1 = m2+m9; // m1 = 8
	cout << "m1 = m2+m9 = "<< m1 << endl;

	m2 = m7-m6; // m2 = -4x^2
	cout << "m2 = m7-m6 = "<< m2 << endl;

	m1 = m8*m1; // m1 = 140x^4
	cout << "m1 = m8*m1 = " << m1 <<endl;

	m1 *= m8; // m1 = 2450x^8
	cout << "m1 *=m8 = m1*m8 = "<< m1 << endl;

	m1 = m1/1225;
	cout << "m1 = m1/1225 = " << m1 << endl; // m1 = 2x^8

	m3 = m1/m2;
	cout << "m3 = m1/m2 = " << m1 << " / " << m2 << " = " << m3 <<endl; // m3 = -0.5x^6

	cout << "m8(3) = " << m8(3) << endl; // m8(3) = 1417.5

	m6 = m6^3;
	cout << "m6 ^ 3 = " << m6 << endl; // m6 = 8x^6

	m1 = m1*0.5;
	cout << "m1*0.5 = " << m1 << endl; // m1 = x^8

	m5 = m5 + m9;
	cout << "m5 += m9 = " << m5 << endl; //m5 = 8

	m6 = m7*4;
	m6 = m6 - m7;
	cout << "m6 -= m7 = " << m6 << endl; //m6 = -6x^2

	m6 = m6 / m7;
	cout << "m6 /= m7 = " << m6 << endl; // m6 = 3


	if(m6 != m5)
	cout << "m6 != m5" << endl;

	if(m4 == m10)
	{
//	m10 = m5;	// m10 = 8
//	m4 = m10;	// m4 = 8
	m4 = m10 = m5;
	cout << "m4 = " << m4 << " , m10 = " << m10 << endl; // 8 , 8
	}

	// m3.getExponent = 6
	// m7.getExponent = 2
	// m6.getExponent = 0
	if(m7.getExponent() == m3.getExponent())
		cout << "deg(m6) = deg(m3) = " << m7.getExponent() << endl; // does not print anything



	m6 = ((((m6+m7)*m5)^2) + m1)*m5;
	cout << "m6 = "<< m6 << endl; // m6 = 520x^4


	cout << "deg(p1) = " << p1.getOrder() << endl;
	cout << "deg(p4) = " << p4.getOrder() << endl;
	cout << "deg(p5) = " << p5.getOrder() << endl;
	cout << "deg(p8) = " << p8.getOrder() << endl;

	p4 = m5 + ((p4 + m6) + p6);
	cout << "p4 = " << p4 << endl; // p4 = 8 - 2x^2 + 520x^4 + 3.5x^5
	p7 = (m1 + p6) + p4.derive();
	p5 = p4 + p7;

	cout << "new p4 = " << p4 << endl; // p4 = -4x + 2080x^3 + 17.5x^4
	cout << "p7 = " << p7 << endl; //-4x -2x^2 + 2080x^3 + 18.5x^4
	cout << "p5 = " << p5 << endl; // -8x -2x^2 + 4160x^3 + 36x^4
	cout << "p5' = " << p5.derive() << endl; // -8 -4x + 12480x^2 + 144x^3


	p4 = p4 - p7 + m1;
	cout << "p4 = " << p4 << endl; // p4 = 2x^4

	p4 = m1 -p4 + p5 - m6;
	cout << "p4 = " << p4 << endl; // p4 = -8-4x+12478x^2+144x^3-519x^4

	p5 = p7; // p5 = -4x -2x^2 + 2080x^3 + 18.5x^4
	p7.derive(); //p7 = -4 -4x + 6240x^2 + 74x^3
	cout << "p5 = " << p5 << endl << "p7 = " << p7 << endl;

	cout << "p1' = " << p1.derive() << endl; // p1' = 0
}